using System;
using System.Runtime.InteropServices;
using System.IO;
using System.Collections.Generic;
using System.Windows.Forms;

namespace SCPutils
{
    /// <summary>
    /// MAPI class used to send emails by calling the default email client
    /// </summary>
    public class MAPI
    {
        /// <summary>
        /// Adds recipients for 'To' property
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public bool AddRecipientTo(string email)
        {
            return AddRecipient(email, HowTo.MAPI_TO);
        }
        /// <summary>
        /// dds recipients for 'Cc' property
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public bool AddRecipientCC(string email)
        {
            return AddRecipient(email, HowTo.MAPI_TO);
        }
        /// <summary>
        /// dds recipients for 'Bcc' property
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public bool AddRecipientBCC(string email)
        {
            return AddRecipient(email, HowTo.MAPI_TO);
        }
        /// <summary>
        /// Adds attachment to email
        /// </summary>
        /// <param name="strAttachmentFileName"></param>
        public void AddAttachment(string strAttachmentFileName)
        {
            m_attachments.Add(strAttachmentFileName);
        }
        /// <summary>
        /// Sends the email by using the default email client and by opening the popup window
        /// </summary>
        /// <param name="strSubject"></param>
        /// <param name="strBody"></param>
        /// <returns></returns>
        public int SendMailPopup(string strSubject, string strBody)
        {
            return SendMail(strSubject, strBody, MAPI_LOGON_UI | MAPI_DIALOG);
        }
        /// <summary>
        /// Sends the email by using the default email client automatically - no popup window
        /// </summary>
        /// <param name="strSubject"></param>
        /// <param name="strBody"></param>
        /// <returns></returns>
        public int SendMailDirect(string strSubject, string strBody)
        {
            return SendMail(strSubject, strBody, MAPI_LOGON_UI);
        }


        [DllImport("MAPI32.DLL")]
        static extern int MAPISendMail(IntPtr sess, IntPtr hwnd, MapiMessage message, int flg, int rsv);

        int SendMail(string strSubject, string strBody, int how)
        {
            MapiMessage msg = new MapiMessage();
            msg.subject = strSubject;
            msg.noteText = strBody;

            msg.recips = GetRecipients(out msg.recipCount);
            msg.files = GetAttachments(out msg.fileCount);

            m_lastError = MAPISendMail(new IntPtr(0), new IntPtr(0), msg, how, 0);
            if (m_lastError > 1)
                MessageBox.Show("MAPISendMail failed! " + GetLastError(), "MAPISendMail");

            Cleanup(ref msg);
            return m_lastError;
        }

        bool AddRecipient(string email, HowTo howTo)
        {
		    MapiRecipDesc recipient = new MapiRecipDesc();

            recipient.recipClass = (int)howTo;
    		recipient.name = email;
		    m_recipients.Add(recipient);

            return true;
        }

        IntPtr GetRecipients(out int recipCount)
        {
            recipCount = 0;
            if (m_recipients.Count == 0)
                return IntPtr.Zero;

            int size = Marshal.SizeOf(typeof(MapiRecipDesc));
            IntPtr intPtr = Marshal.AllocHGlobal(m_recipients.Count * size);

            int ptr = (int)intPtr;
            foreach (MapiRecipDesc mapiDesc in m_recipients)
            {
                Marshal.StructureToPtr(mapiDesc, (IntPtr)ptr, false);
                ptr += size;
            }

            recipCount = m_recipients.Count;
            return intPtr;
        }

        IntPtr GetAttachments(out int fileCount)
        {
            fileCount = 0;
            if (m_attachments == null)
                return IntPtr.Zero;

            if ((m_attachments.Count <= 0) || (m_attachments.Count > maxAttachments))
                return IntPtr.Zero;

            int size = Marshal.SizeOf(typeof(MapiFileDesc));
            IntPtr intPtr = Marshal.AllocHGlobal(m_attachments.Count * size);

            MapiFileDesc mapiFileDesc = new MapiFileDesc();
            mapiFileDesc.position = -1;
            int ptr = (int)intPtr;
            
            foreach (string strAttachment in m_attachments)
            {
                mapiFileDesc.name = Path.GetFileName(strAttachment);
                mapiFileDesc.path = strAttachment;
                Marshal.StructureToPtr(mapiFileDesc, (IntPtr)ptr, false);
                ptr += size;
            }

            fileCount = m_attachments.Count;
            return intPtr;
        }

        void Cleanup(ref MapiMessage msg)
        {
            int size = Marshal.SizeOf(typeof(MapiRecipDesc));
            int ptr = 0;

            if (msg.recips != IntPtr.Zero)
            {
                ptr = (int)msg.recips;
                for (int i = 0; i < msg.recipCount; i++)
                {
                    Marshal.DestroyStructure((IntPtr)ptr, typeof(MapiRecipDesc));
                    ptr += size;
                }
                Marshal.FreeHGlobal(msg.recips);
            }

            if (msg.files != IntPtr.Zero)
            {
                size = Marshal.SizeOf(typeof(MapiFileDesc));

                ptr = (int)msg.files;
                for (int i = 0; i < msg.fileCount; i++)
                {
                    Marshal.DestroyStructure((IntPtr)ptr, typeof(MapiFileDesc));
                    ptr += size;
                }
                Marshal.FreeHGlobal(msg.files);
            }
            
            m_recipients.Clear();
            m_attachments.Clear();
            m_lastError = 0;
        }
        /// <summary>
        /// Gets the last Error message
        /// </summary>
        /// <returns>String representing the error message</returns>
        public string GetLastError()
		{
		    if (m_lastError <= 26)
			    return errors[ m_lastError ];
		    return "MAPI error [" + m_lastError.ToString() + "]";
		}

	    readonly string[] errors = new string[] {
		"OK [0]", "User abort [1]", "General MAPI failure [2]", "MAPI login failure [3]",
		"Disk full [4]", "Insufficient memory [5]", "Access denied [6]", "-unknown- [7]",
		"Too many sessions [8]", "Too many files were specified [9]", "Too many recipients were specified [10]", "A specified attachment was not found [11]",
		"Attachment open failure [12]", "Attachment write failure [13]", "Unknown recipient [14]", "Bad recipient type [15]",
		"No messages [16]", "Invalid message [17]", "Text too large [18]", "Invalid session [19]",
		"Type not supported [20]", "A recipient was specified ambiguously [21]", "Message in use [22]", "Network failure [23]",
		"Invalid edit fields [24]", "Invalid recipients [25]", "Not supported [26]" 
		};


        List<MapiRecipDesc> m_recipients	= new List<MapiRecipDesc>();
        List<string> m_attachments	= new List<string>();
        int m_lastError = 0;

        const int MAPI_LOGON_UI = 0x00000001;
        const int MAPI_DIALOG = 0x00000008;
        const int maxAttachments = 20;

        enum HowTo{MAPI_ORIG=0, MAPI_TO, MAPI_CC, MAPI_BCC};
    }

    /// <summary>
    /// Manage data related to MapiMessage (Email)
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class MapiMessage
    {
        /// <summary>
        /// 
        /// </summary>
        public int reserved;
        /// <summary>
        /// Subject of the Email message.
        /// </summary>
        public string subject;
        /// <summary>
        /// Body of the Email message.
        /// </summary>
        public string noteText;
        /// <summary>
        /// Gets or sets the string indicating a non-IPM type of message
        /// </summary>
        public string messageType;
        /// <summary>
        /// Returns the date when the message was received. 
        /// </summary>
        public string dateReceived;
        /// <summary>
        /// Gets or sets the string identifying the conversation thread to which the message belongs.
        /// </summary>
        public string conversationID;
        /// <summary>
        /// Set or get a bitmask of message status flags. 
        /// </summary>
        public int flags;
        /// <summary>
        /// 
        /// </summary>
        public IntPtr originator;
        /// <summary>
        /// Number of recipients  (email addresses)
        /// </summary>
        public int recipCount;
        /// <summary>
        /// List of recipients  (email addresses)
        /// </summary>
        public IntPtr recips;
        /// <summary>
        /// Number of attachments
        /// </summary>
        public int fileCount;
        /// <summary>
        /// Collection of attachments
        /// </summary>
        public IntPtr files;
    }

    /// <summary>
    /// The MapiFileDesc class gets and sets attached files to messages.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class MapiFileDesc
    {
        /// <summary>
        /// 
        /// </summary>
        public int reserved;
        /// <summary>
        /// 
        /// </summary>
        public int flags;
        /// <summary>
        /// Position of the file inside the attachment's colection
        /// </summary>
        public int position;
        /// <summary>
        /// Path pointint to a file on disk
        /// </summary>
        public string path;
        /// <summary>
        /// Name of the file
        /// </summary>
        public string name;
        /// <summary>
        /// 
        /// </summary>
        public IntPtr type;
    }

    /// <summary>
    /// Describes an email recipient (destination address)
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
    public class MapiRecipDesc
	{
        /// <summary>
        /// 
        /// </summary>
	    public int		reserved;
        /// <summary>
        /// 
        /// </summary>
	    public int		recipClass;
        /// <summary>
        /// Email address or name
        /// </summary>
	    public string	name;
        /// <summary>
        /// Email address
        /// </summary>
	    public string	address;
        /// <summary>
        /// 
        /// </summary>
	    public int		eIDSize;
        /// <summary>
        /// 
        /// </summary>
	    public IntPtr	entryID;
	}
}
